/*
 	Tool for Path Finding Algorithm Testing (ToPFAT) 
 	
    Copyright (C) 2009, Teemu Turunen and Arttu Viljakainen 
    Published under GNU General Public License V3 
    (see http://www.gnu.org/licenses/licenses.html)
    
    This file is part of Tool for Path Finding Algorithm Testing.

    Tool for Path Finding Algorithm Testing is free software: you can 
    redistribute it and/or modify it under the terms of the GNU General 
    Public License as published by the Free Software Foundation, 
    either version 3 of the License, or (at your option) any later version.

    Tool for Path Finding Algorithm Testing is distributed in the hope that it
    will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 
    Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Tool for Path Finding Algorithm Testing. If not, see         <http://www.gnu.org/licenses/>.
 */

package algorithms.astar;

import java.util.Vector;

import pathfinding.gui.Map;
import algorithms.astar.heuristics.DiagonalDistance;
import algorithms.astar.heuristics.EuclideanDistance;
import algorithms.astar.heuristics.HCostHeuristic;
import algorithms.astar.heuristics.ManhattanDistance;

/**
 * Class for holding information and methods for calculating
 * costs for A* moves.
 * 
 * Includes methods for calculating G cost 
 * and H cost (see some A* tutorial for more information).
 * 
 * Also includes possibility to change and add new H cost heuristics to be used.
 * 
 * @author teturun
 *
 */
public class AStarCost {

	public final static int NORMAL_MOVE = 10;
	public final static int DIAGONAL_MOVE = 14;
	
	private Vector<HCostHeuristic> heuristics;
	private int index;
	
	/**
	 * Constructor for AStarCost.
	 */
	public AStarCost() {
		this.heuristics = new Vector<HCostHeuristic>();
		
		this.addHCostHeuristic(new DiagonalDistance());
		this.addHCostHeuristic(new EuclideanDistance());
		this.addHCostHeuristic(new ManhattanDistance());
		
		this.index = 0;
	}

	/**
	 * Add new H cost heuristic.
	 * 
	 * @param heuristic H cost heuristic
	 */
	public void addHCostHeuristic(HCostHeuristic heuristic) {
		
		if (! this.heuristics.contains(heuristic)) {
			this.heuristics.add(heuristic);
		}
		
	}
	
	/**
	 * Remove H cost heuristic.
	 * 
	 * @param heuristic H cost heuristic
	 * @return true, if heuristic was removed, false if not
	 */
	public boolean removeHCostHeuristic(HCostHeuristic heuristic) {
		return this.heuristics.remove(heuristic);
	}
	
	/**
	 * Returns all heuristics added.
	 * 
	 * @return Vector of H cost heuristics
	 */
	public Vector<HCostHeuristic> getHeuristics() {
		return this.heuristics;
	}
	
	/**
	 * Returns the H cost heuristic that is used at the moment.
	 * 
	 * @return the H cost heuristic that is used at the moment
	 */
	public HCostHeuristic getHCostHeuristic() {
		if (this.index >= 0 && this.index < this.heuristics.size())
			return this.heuristics.get(this.index);
		else
			return null;
	}
	
	/**
	 * Set H cost heuristic that is to be used.
	 * 
	 * @param heuristic H cost heuristic that is to be used
	 */
	public void setHCostHeuristic(HCostHeuristic heuristic) {
		this.index = heuristics.indexOf(heuristic);
		
		if (this.index == -1) {
			this.heuristics.add(heuristic);
			this.setHCostHeuristic(heuristic);
		}
	}
	
	/**
	 * Calculates G cost (see some A* tutorial for more information).
	 * 
	 * @param start Node to which the G cost will be calculated
	 * @param parent start Node's parent Node
	 * @param map Map object the path finding algorithm uses
	 * @return calculated G cost
	 */
	public int calculateGCost(Node start, Node parent, Map map) {
		
		int extraCost = map.getCostForCell(start.getLocation().y, start.getLocation().x);
		
		if (parent == null) {
			return extraCost + AStarCost.NORMAL_MOVE;
		}
		else {
			int gCost = parent.getGCost();
			
			if (start.isDiagonalWith(parent)) {
				return (extraCost + gCost + AStarCost.DIAGONAL_MOVE);
			}
			else {
				return (extraCost + gCost + AStarCost.NORMAL_MOVE);
			}
			
		}
		 
	}
	
	/**
	 * Calculates the H cost (see some A* tutorial for more information)
	 * according to set H cost heuristic.
	 * 
	 * @param start Node to which the H cost will be calculated
	 * @param goal Node that is the set goal for search
	 * @return calculated H cost
	 */
	public int calculateHCost(Node start, Node goal) {
		return this.getHCostHeuristic().calculateCost(start, goal);
	}
	
}
